<!DOCTYPE html>
<html>

<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<!--
Final version of dashboard for Quantified Self meeting in London
http://www.meetup.com/LondonQS/events/68812532/ 

Author silvano.luciani@gmail.com (Silvano Luciani)
-->

  <head>
    <title>Final Dashboard</title>
    <script type="text/javascript"
      src='https://www.google.com/jsapi?autoload={
        "modules": [{
          "name": "visualization",
          "version": "1",
          "packages": ["controls"]
        }]
      }'>
    </script>
    <script
      src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
    </script>
    <script type="text/javascript" src="date-en-US.js"></script>
  </head>
  <body>
    <div id="dashboard_div">
      <div id="main_chart_div"></div>
      <div id="filter_div" style="width: 500px; height: 50px; margin-left: 100px"></div>
      <div style="display:-webkit-box; -webkit-box-orient:horizontal;">
        <div id="pie_chart_div"></div><div id="column_chart_div">
      </div>
    </div>
    <script type="text/javascript">
    $.getJSON('data.json', function(data) {
      
      // The data table for the main chart
      var dataTable = new google.visualization.DataTable();
      dataTable.addColumn('datetime', 'Posting Date');
      dataTable.addColumn('number', 'Comments');
      dataTable.addColumn('number', 'Plusones');
      dataTable.addColumn('number', 'Reshares');
      dataTable.addColumn('number', 'EI');
      dataTable.addColumn('string', 'Post URL');
      
      // The data tables for the post types and post times charts
      var postTypesDataTable = createPostTypesDataTable();
      var postTimesDataTable = createPostTimesDataTable();
      
      // We use this datastructures to compute the data for the post types and
      // post times charts
      var postTypesCount = createPostTypesCount();
      var postTimesCount = createPostTimesCount()
      
      // Extract the data we want to visualize
      $.each(data.items, function(key, val) {
        var date = Date.parseExact(
          val.published, "yyyy-MM-ddTHH:mm:ss.sssZ");
        // Line below because the date is not always parsed correctly
        if(date) {
          dataTable.addRow([
            date,
            val.object.replies.totalItems,
            val.object.plusoners.totalItems,
            val.object.resharers.totalItems,
            calculateEI(val.object),
            val.url
          ]);
          // Increment the counter for post of this type
          postTypesCount[val.verb]++;
          // Increment the counter for the hour range
          var hour = date.getHours();
          if(hour >= 0 && hour < 6) {
            postTimesCount['0-6']++;
          } else if(hour >= 6 && hour < 12) {
            postTimesCount['6-12']++;
          } else if(hour >= 12 && hour < 18) {
            postTimesCount['12-18']++;
          } else if(hour >= 18 && hour < 24) {
            postTimesCount['18-24']++;
          }
        }
      });
      
      // Add the values for the post types
      postTypesDataTable.addRow(['Post', postTypesCount['post']]);
      postTypesDataTable.addRow(['Reshare', postTypesCount['share']]);
      postTypesDataTable.addRow(['Checkin', postTypesCount['checkin']]);
      
      // Add the values for the post times
      postTimesDataTable.addRow([
        'Posts per Hour', postTimesCount['0-6'],
        postTimesCount['6-12'], postTimesCount['12-18'],
        postTimesCount['18-24']
      ]);
      
      // We need a dashboard to bind the filter to the main chart
      var dashboard = new google.visualization.Dashboard(
        document.getElementById('dashboard_div'));
      
      // Wrapper for our date range filter
      var slider = new google.visualization.ControlWrapper({
        'controlType': 'ChartRangeFilter',
        'containerId': 'filter_div',
        'options': {
          'filterColumnIndex': 0,
          'ui': {
            'chartType': 'LineChart',
            'chartView': {
              'columns': [0, 4]
            },
          }
        }
      });
      
      // Wrapper for the line chart that shows the engagement on posts
      var mainChart = new google.visualization.ChartWrapper({
        'chartType': 'LineChart',
        'options': {
          'title': 'Engagement on posts',
          'width': 700,
          'height': 300,
          'animation':{
            'duration': 400,
            'easing': 'out'
          },
        },
        'containerId': 'main_chart_div',
        'view': {'columns': [0, 1, 2, 3]}
      });
      
      // Wrapper for the pie chart showing the post types composition
      var pieChart = new google.visualization.ChartWrapper({
        'chartType': 'PieChart',
        'dataTable': postTypesDataTable,
        'options': {
          'title': 'Post Types',
          'is3D': true,
          'width': 350,
          'height': 300,
          'animation':{
            'duration': 400,
            'easing': 'out'
          },
        },
        'containerId': 'pie_chart_div'
      });
      
      // Wrapper for the column chart showing the posting times habits
      var columnChart = new google.visualization.ChartWrapper({
        'chartType': 'ColumnChart',
        'dataTable': postTimesDataTable,
        'options': {
          'title': 'Posting Time Habits',
          'width': 350,
          'height': 300,
          'animation':{
            'duration': 400,
            'easing': 'out'
          },
          'legend': {
            'position': 'none',
          },
        },
        'containerId': 'column_chart_div'
      });
      
      // We bind the filter to the main chart
      dashboard.bind(slider, mainChart);
      // We draw the dashboard
      dashboard.draw(dataTable);
      // We need to draw the other charts separately
      pieChart.draw();
      columnChart.getOptions().legend = null;
      columnChart.draw();
      
      // Update the pie chart when the date range filter changes
      google.visualization.events.addListener(slider, 'statechange', function(e) {
          updatePieChart(data, pieChart, slider.getState())});
      
      // Update the column chart when the date range filter changes
      google.visualization.events.addListener(slider, 'statechange', function(e) {
          updateColumnChart(data, columnChart, slider.getState())});
      
      // Change the type of chart when the range is shorter than one month
      google.visualization.events.addListener(slider, 'statechange', function(e) {
          switchMainChart(mainChart, slider.getState())});
      
      // When a plot om the main chart is clicked, open a new window containing the post
      google.visualization.events.addListener(mainChart, 'select', function(e) {
          var chart = mainChart.getChart()
          var selection = chart.getSelection();
          var item = selection[0];
          var postURL = dataTable.getValue(item.row, 5);
          window.open(postURL, '_blank');
      });      
    });
    
    // Engagement Index: sum of actions on the post
    // Used in the range filter internal chart to express the trend
    function calculateEI(object) {
      return object.replies.totalItems +
          object.plusoners.totalItems +
          object.resharers.totalItems;   
    }
    
    // Recompute values for the pie chart when the date range changes
    function updatePieChart(data, pieChart, state) {
      var postTypesCount = createPostTypesCount();
      
      $.each(data.items, function(key, val) {
        var date = Date.parseExact(
          val.published, "yyyy-MM-ddTHH:mm:ss.sssZ");
        var startDate = Date.parse(state.range.start);
        var endDate = Date.parse(state.range.end);
        if(date && date.isAfter(startDate) && date.isBefore(endDate)) {
          postTypesCount[val.verb]++;
        }
      });
      var postTypesDataTable = createPostTypesDataTable();
      
      postTypesDataTable.addRow(['Post', postTypesCount['post']]);
      postTypesDataTable.addRow(['Reshare', postTypesCount['share']]);
      postTypesDataTable.addRow(['Checkin', postTypesCount['checkin']]);
      
      pieChart.setDataTable(postTypesDataTable);
      pieChart.draw();
    }

    // Recompute values for the column chart when the date range changes
    function updateColumnChart(data, columnChart, state) {
      var postTimesCount = createPostTimesCount();
      
      $.each(data.items, function(key, val) {
        var date = Date.parseExact(
          val.published, "yyyy-MM-ddTHH:mm:ss.sssZ");
        var startDate = Date.parse(state.range.start);
        var endDate = Date.parse(state.range.end);
        if(date && date.isAfter(startDate) && date.isBefore(endDate)) {
          var hour = date.getHours();
          if(hour >= 0 && hour < 6) {
            postTimesCount['0-6']++;
          } else if(hour >= 6 && hour < 12) {
            postTimesCount['6-12']++;
          } else if(hour >= 12 && hour < 18) {
            postTimesCount['12-18']++;
          } else if(hour >= 18 && hour < 24) {
            postTimesCount['18-24']++;
          }
        }
      });
      var postTimesDataTable = createPostTimesDataTable();
      
      postTimesDataTable.addRow([
        'Posts per Hour', postTimesCount['0-6'],
        postTimesCount['6-12'], postTimesCount['12-18'],
        postTimesCount['18-24']
      ]);
      
      columnChart.setDataTable(postTimesDataTable);
      columnChart.draw();
    }
    
    // Switch the type of the main chart according to the lenght of the date
    // range
    function switchMainChart(mainChart, state) {
      var startDate = Date.parse(state.range.start);
      var endDate = Date.parse(state.range.end);
      if(startDate.add({months: 1}).isAfter(endDate)) {
        if(mainChart.getChartType() == 'LineChart') {
          mainChart.setChartType('AreaChart');
          mainChart.draw();
        }
      } else {
        if(mainChart.getChartType() == 'AreaChart') {
           mainChart.setChartType('LineChart');
           mainChart.draw();
        }
      }
    }
    
    // The functions below take create some data structures
    function createPostTypesCount() {
      return {
        'post': 0,
        'share': 0,
        'checkin': 0
      };
    }
    
    function createPostTypesDataTable() {
      var postTypesDataTable = new google.visualization.DataTable();
      postTypesDataTable.addColumn('string', 'Post Type');
      postTypesDataTable.addColumn('number', 'Number of Posts');
      return postTypesDataTable;
    }
    
    function createPostTimesCount() {
      return {
        '0-6': 0,
        '6-12': 0,
        '12-18': 0,
        '18-24': 0
      };
    }
    
    function createPostTimesDataTable() {
      var postTimesDataTable = new google.visualization.DataTable();
      postTimesDataTable.addColumn('string', 'Post Time');
      postTimesDataTable.addColumn('number', '0-6');
      postTimesDataTable.addColumn('number', '6-12');
      postTimesDataTable.addColumn('number', '12-18');
      postTimesDataTable.addColumn('number', '18-24');
      return postTimesDataTable;
    }
    </script>
  </body>
</html>